﻿namespace Creek.UI.EFML
{
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using System.Xml;

    using Creek.UI.EFML.Base;
    using Creek.UI.EFML.Base.EFML.Elements;
    using Creek.UI.EFML.Base.EFML.Processors;
    using Creek.UI.EFML.Base.Exceptions;
    using Creek.UI.EFML.UI_Elements;

    using EFML_Runtime.Internal;

    using DefaultEventProvider = DefaultEventProvider;

    public class Builder
    {
        #region Fields

        public Dictionary<string, string> Strings = new Dictionary<string, string>();

        public Document document = new Document();

        private readonly ProcessorCollection Processors = new ProcessorCollection();

        #endregion

        #region Constructors and Destructors

        public Builder(string strings)
        {
            var sp = new StringParser();
            sp.Load(strings);

            this.Strings = sp.Strings;

            this.AddProcessor<ImageProcessor>();
            this.AddProcessor<LinkProcessor>();
            this.AddProcessor<InputProcessor>();
            this.AddProcessor<LabelProcessor>();
            this.AddProcessor<ObjectProcessor>();
            this.AddProcessor<FlashProcessor>();
            this.AddProcessor<DivProcessor>();
            this.AddProcessor<GroupProcessor>();
            this.AddProcessor<DropDownProcessor>();
            this.AddProcessor<AudioProcessor>();
            this.AddProcessor<VideoProcessor>();
            this.AddProcessor<TableProcessor>();
            this.AddProcessor<TabControlProcessor>();
            this.AddProcessor<LineProcessor>();
            this.AddProcessor<NavigatorProcessor>();
            this.AddProcessor<BreakProcessor>();
            this.AddProcessor<WebViewProcessor>();
        }

        #endregion

        #region Public Methods and Operators

        [DebuggerStepThrough]
        public void Load(byte[] buffer)
        {
            this.Load(Encoding.ASCII.GetString(buffer));
        }

        [DebuggerStepThrough]
        public void Load(string efml)
        {
            EFMLDocument doc = EFMLDocument.Load(efml);

            //new SourceElement().Process(document.Header, doc);
            new MetaElement().Process(this.document.Header.Meta, doc);
            new ScriptElement().Process(this.document.Header.Scripts, doc);
            new StyleElement().Process(this.document.Header.Styles, doc);

            this.UiBaseElement(doc.Body.ChildNodes, this.document.Body);
        }

        #endregion

        #region Methods

        [DebuggerStepThrough]
        internal void UiBaseElement(XmlNodeList t, List<ElementBase> parent)
        {
            foreach (XmlNode markupTag in t)
            {
                if (markupTag is XmlComment)
                {
                    continue;
                }
                var e = new UiElement();

                new DefaultEventProvider().Resolve(e, markupTag);

                if (this.Processors[markupTag.Name] != null)
                {
                    this.Processors[markupTag.Name].Process(out e, markupTag, this);
                }
                else
                {
                    throw new EfmlException("'" + markupTag.Name + "'-Tag is not found");
                }

                if (markupTag.HasAttribute("id"))
                {
                    e.ID = markupTag.GetAttributeByName("id");
                }
                else
                {
                    throw new EfmlException("'id' attribute is required on '" + markupTag.Name + "'");
                }

                if (markupTag.HasAttribute("value"))
                {
                    string l = markupTag.GetAttributeByName("value");

                    e.value = l;
                }
                else
                {
                    e.value = markupTag.InnerText;
                }

                if (e.value.StartsWith(@"@string/"))
                {
                    e.value = this.Strings[e.value.Remove(0, @"@string/".Length)];
                }

                if (e is Dropdown)
                {
                    var ee = e as Dropdown;
                    for (int index = 0; index < ee.Childs.Count; index++)
                    {
                        string child = ee.Childs[index];
                        if (child.StartsWith(@"@string/"))
                        {
                            child = this.Strings[child.Remove(0, @"@string/".Length)];
                        }
                        ee.Childs[index] = child;
                    }
                }

                if (e.GetType().Name != typeof(UiElement).Name)
                {
                    parent.Add(e);
                }
            }
        }

        private void AddProcessor(ElementProcessor p)
        {
            this.Processors.Add(new KeyValuePair<string, ElementProcessor>(p.Tagname, p));
        }

        private void AddProcessor<T>() where T : ElementProcessor, new()
        {
            this.AddProcessor(new T());
        }

        #endregion
    }
}